package io.github.uicdb.badskill;

import io.github.uicdb.badskill.api.OptionArgType;
import io.github.uicdb.badskill.block.DisableBlock;
import io.github.uicdb.badskill.block.DisableEntity;
import io.github.uicdb.badskill.block.OptionButton;
import io.github.uicdb.badskill.block.OptionPressurePlate;
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.RangedAttribute;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
import net.minecraftforge.event.entity.EntityAttributeModificationEvent;
import net.minecraftforge.event.entity.living.LivingDamageEvent;
import net.minecraftforge.event.entity.living.LivingHealEvent;
import net.minecraftforge.event.entity.living.LivingHurtEvent;
import net.minecraftforge.event.entity.player.CriticalHitEvent;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;

import java.util.function.Consumer;

public class BSRegistries {
    private static final DeferredRegister<CreativeModeTab> TAB_DEFERRED_REGISTER = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, BadSkill.MODID);
    public static final RegistryObject<CreativeModeTab> MOD_TAB = TAB_DEFERRED_REGISTER.register(BadSkill.MODID, () -> CreativeModeTab.builder()
            .icon(() -> Blocks.BEDROCK.asItem().getDefaultInstance())
            .withTabsAfter(CreativeModeTabs.SPAWN_EGGS)
            .title(Component.translatable("tab.badskill.name"))
            .build()
    );
    private static final DeferredRegister<Block> BLOCK_DEFERRED_REGISTER = DeferredRegister.create(ForgeRegistries.BLOCKS, BadSkill.MODID);

    public static final RegistryObject<DisableBlock> BLOCK_DISABLE = BLOCK_DEFERRED_REGISTER.register("disable", () -> new DisableBlock(BlockBehaviour.Properties.copy(Blocks.BEDROCK)));
    public static final RegistryObject<OptionButton> BLOCK_OPTION_BUTTON = BLOCK_DEFERRED_REGISTER.register("option_button", () -> new OptionButton(BlockBehaviour.Properties.copy(Blocks.STONE_BUTTON)));
    public static final RegistryObject<OptionPressurePlate> BLOCK_OPTION_PRESSURE_PLATE = BLOCK_DEFERRED_REGISTER.register("option_pressure_plate", () -> new OptionPressurePlate(BlockBehaviour.Properties.copy(Blocks.STONE_BUTTON)));

    private static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITY_TYPE_DEFERRED_REGISTER = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, BadSkill.MODID);

    public static final RegistryObject<BlockEntityType<DisableEntity>> BLOCKENTITY_DISABLE = BLOCK_ENTITY_TYPE_DEFERRED_REGISTER.register("disable", () -> BlockEntityType.Builder.of(DisableEntity::new, BLOCK_DISABLE.get()).build(null));

    private static final DeferredRegister<Item> ITEM_DEFERRED_REGISTER = DeferredRegister.create(ForgeRegistries.ITEMS, BadSkill.MODID);

    public static final RegistryObject<BlockItem> ITEM_DISABLE = ITEM_DEFERRED_REGISTER.register("disable", () -> new BlockItem(BLOCK_DISABLE.get(), new Item.Properties()));
    public static final RegistryObject<BlockItem> ITEM_OPTION_BUTTON = ITEM_DEFERRED_REGISTER.register("option_button", () -> new BlockItem(BLOCK_OPTION_BUTTON.get(), new Item.Properties()));
    public static final RegistryObject<BlockItem> ITEM_OPTION_PRESSURE_PLATE = ITEM_DEFERRED_REGISTER.register("option_pressure_plate", () -> new BlockItem(BLOCK_OPTION_PRESSURE_PLATE.get(), new Item.Properties()));

    private static final DeferredRegister<Attribute> ATTRIBUTE_DEFERRED_REGISTER = DeferredRegister.create(ForgeRegistries.ATTRIBUTES, BadSkill.MODID);

    public static final RegistryObject<Attribute> ATTRIBUTE_CRIT = ATTRIBUTE_DEFERRED_REGISTER.register("player.crit", () -> new RangedAttribute("player.crit", 5, Double.NEGATIVE_INFINITY, Double.MAX_VALUE).setSyncable(false));
    public static final RegistryObject<Attribute> ATTRIBUTE_CRIT_EFFECT = ATTRIBUTE_DEFERRED_REGISTER.register("player.crit_effect", () -> new RangedAttribute("player.crit_effect", 50, 0, Double.MAX_VALUE).setSyncable(false));
    public static final RegistryObject<Attribute> ATTRIBUTE_HEALTH_STEAL = ATTRIBUTE_DEFERRED_REGISTER.register("player.health_steal", () -> new RangedAttribute("player.health_steal", 0, 0, 100).setSyncable(false));
    public static final RegistryObject<Attribute> ATTRIBUTE_RECOVER_EFFECT = ATTRIBUTE_DEFERRED_REGISTER.register("player.recover_effect", () -> new RangedAttribute("player.recover_effect", 0, 0, Double.MAX_VALUE).setSyncable(false));

    private static final DeferredRegister<ArgumentTypeInfo<?, ?>> ARGUMENT_TYPE_INFO_DEFERRED_REGISTER = DeferredRegister.create(ForgeRegistries.COMMAND_ARGUMENT_TYPES, BadSkill.MODID);
    public static final RegistryObject<ArgumentTypeInfo<?, ?>> ARG_TYPE_OPTION = ARGUMENT_TYPE_INFO_DEFERRED_REGISTER.register("option_type", () -> ArgumentTypeInfos.registerByClass(OptionArgType.class,
            SingletonArgumentInfo.contextFree(OptionArgType::option)));



    public static void register(IEventBus bus) {
        TAB_DEFERRED_REGISTER.register(bus);
        ATTRIBUTE_DEFERRED_REGISTER.register(bus);
        BLOCK_DEFERRED_REGISTER.register(bus);
        BLOCK_ENTITY_TYPE_DEFERRED_REGISTER.register(bus);
        ITEM_DEFERRED_REGISTER.register(bus);
        ARGUMENT_TYPE_INFO_DEFERRED_REGISTER.register(bus);
        bus.addListener(BSRegistries::buildCreativeTab);
        bus.addListener(BSRegistries::attachEntityAttribute);
        MinecraftForge.EVENT_BUS.addListener(EventPriority.LOWEST, BSRegistries::onCritHitEvent);
        MinecraftForge.EVENT_BUS.addListener(EventPriority.LOWEST, BSRegistries::onEntityCure);
        MinecraftForge.EVENT_BUS.addListener(EventPriority.LOWEST, BSRegistries::onLivingDamage);
    }

    private static void buildCreativeTab(BuildCreativeModeTabContentsEvent event) {
        if (event.getTab() == BSRegistries.MOD_TAB.get()) {
            for (RegistryObject<Item> entry : ITEM_DEFERRED_REGISTER.getEntries()) {
                event.accept(entry);
            }
        }
    }

    private static void attachEntityAttribute(EntityAttributeModificationEvent event) {
        event.add(EntityType.PLAYER, ATTRIBUTE_CRIT.get());
        event.add(EntityType.PLAYER, ATTRIBUTE_CRIT_EFFECT.get());
        for (EntityType<? extends LivingEntity> type : event.getTypes()) {
            addIfNotExists(event,type, ATTRIBUTE_RECOVER_EFFECT.get());
            addIfNotExists(event,type, ATTRIBUTE_HEALTH_STEAL.get());
        }
    }

    private static <T extends LivingEntity> void addIfNotExists(EntityAttributeModificationEvent event, EntityType<T> type, Attribute attribute){
        if(!event.has(type,attribute))event.add(type,attribute);
    }
    private static void onLivingDamage(LivingDamageEvent event) {
        if (!event.isCanceled()){
            runIfLivingDamage(event.getSource(), living -> {
                double amount = event.getAmount() * living.getAttributeValue(ATTRIBUTE_HEALTH_STEAL.get());
                if (amount > 0)
                    living.heal((float) amount);
            });
        }
    }
    private static void runIfLivingDamage(DamageSource source, Consumer<LivingEntity> consumer) {
        LivingEntity living = null;
        if (source.getEntity() instanceof LivingEntity p) {
            living = p;
        } else if (source.getDirectEntity() instanceof LivingEntity p) {
            living = p;
        }
        if (living != null) {
            consumer.accept(living);
        }
    }
    private static void onEntityCure(LivingHealEvent event) {
        double effective = event.getEntity().getAttributeValue(ATTRIBUTE_RECOVER_EFFECT.get()) / 100d;
        event.setAmount((float) (event.getAmount() * (1d + effective)));
    }

    private static void onCritHitEvent(CriticalHitEvent event) {
        Player player = event.getEntity();
        double crit = player.getAttributeValue(ATTRIBUTE_CRIT.get()) / 100d;
        if (player.getRandom().nextDouble() <= crit) {
            event.setResult(Event.Result.ALLOW);
        }
        double critEffect = player.getAttributeValue(ATTRIBUTE_CRIT_EFFECT.get()) / 100d;
        event.setDamageModifier((float) critEffect);
    }
}
